<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>豆瓣书籍数据可视化</title>
    <!-- 引入 Tailwind CSS -->
    <script src="https://cdn.tailwindcss.com"></script>
    <!-- 引入 Font Awesome -->
    <link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
    <!-- 引入 D3.js -->
    <script src="https://d3js.org/d3.v7.min.js"></script>
    
    <!-- 配置 Tailwind -->
    <script>
        tailwind.config = {
            theme: {
                extend: {
                    colors: {
                        primary: '#165DFF',
                        secondary: '#FF7D00',
                        success: '#00B42A',
                        danger: '#F53F3F',
                        warning: '#FF7D00',
                        info: '#86909C',
                        light: '#F2F3F5',
                        dark: '#1D2129',
                    },
                    fontFamily: {
                        sans: ['Inter', 'system-ui', 'sans-serif'],
                    },
                }
            }
        }
    </script>
    
    <style type="text/tailwindcss">
        @layer utilities {
            .content-auto {
                content-visibility: auto;
            }
            .text-shadow {
                text-shadow: 0 2px 4px rgba(0,0,0,0.1);
            }
            .card-hover {
                transition: all 0.3s ease;
            }
            .card-hover:hover {
                transform: translateY(-5px);
                box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
            }
            .scrollbar-hide::-webkit-scrollbar {
                display: none;
            }
            .scrollbar-hide {
                -ms-overflow-style: none;
                scrollbar-width: none;
            }
        }
    </style>
    
    <style>
        /* 定义书籍分类的颜色 */
        .category-circle {
            cursor: pointer;
            transition: all 0.3s ease;
        }
        
        .category-circle:hover {
            stroke: #000;
            stroke-width: 2px;
        }
        
        .author-circle {
            cursor: pointer;
            transition: all 0.3s ease;
        }
        
        .author-circle:hover {
            stroke: #000;
            stroke-width: 2px;
        }
        
        /* 书籍卡片动画 */
        .book-card {
            opacity: 0;
            transform: translateY(20px);
            transition: opacity 0.5s ease, transform 0.5s ease;
        }
        
        .book-card.visible {
            opacity: 1;
            transform: translateY(0);
        }
        
        /* 滚动条样式 */
        ::-webkit-scrollbar {
            width: 8px;
            height: 8px;
        }
        
        ::-webkit-scrollbar-track {
            background: #f1f1f1;
            border-radius: 4px;
        }
        
        ::-webkit-scrollbar-thumb {
            background: #c1c1c1;
            border-radius: 4px;
        }
        
        ::-webkit-scrollbar-thumb:hover {
            background: #a8a8a8;
        }
    </style>
</head>
<body class="bg-gray-50 font-sans">
    <!-- 导航栏 -->
    <nav class="bg-white shadow-md fixed w-full z-50 transition-all duration-300" id="navbar">
        <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
            <div class="flex justify-between h-16">
                <div class="flex items-center">
                    <i class="fa fa-book text-primary text-2xl mr-2"></i>
                    <span class="text-xl font-bold text-gray-800">豆瓣书籍可视化</span>
                </div>
                <div class="hidden md:flex items-center space-x-8">
                    <a href="#overview" class="text-gray-600 hover:text-primary transition-colors">概览</a>
                    <a href="#book-list" class="text-gray-600 hover:text-primary transition-colors">书籍列表</a>
                    <a href="#category-chart" class="text-gray-600 hover:text-primary transition-colors">分类统计</a>
                    <a href="#author-chart" class="text-gray-600 hover:text-primary transition-colors">作者统计</a>
                    <a href="#about" class="text-gray-600 hover:text-primary transition-colors">关于</a>
                </div>
                <div class="md:hidden flex items-center">
                    <button id="menu-toggle" class="text-gray-500 hover:text-gray-600 focus:outline-none">
                        <i class="fa fa-bars text-xl"></i>
                    </button>
                </div>
            </div>
        </div>
        
        <!-- 移动端菜单 -->
        <div id="mobile-menu" class="hidden md:hidden bg-white shadow-lg">
            <div class="px-2 pt-2 pb-3 space-y-1 sm:px-3">
                <a href="#overview" class="block px-3 py-2 rounded-md text-base font-medium text-gray-700 hover:text-primary hover:bg-gray-50">概览</a>
                <a href="#book-list" class="block px-3 py-2 rounded-md text-base font-medium text-gray-700 hover:text-primary hover:bg-gray-50">书籍列表</a>
                <a href="#category-chart" class="block px-3 py-2 rounded-md text-base font-medium text-gray-700 hover:text-primary hover:bg-gray-50">分类统计</a>
                <a href="#author-chart" class="block px-3 py-2 rounded-md text-base font-medium text-gray-700 hover:text-primary hover:bg-gray-50">作者统计</a>
                <a href="#about" class="block px-3 py-2 rounded-md text-base font-medium text-gray-700 hover:text-primary hover:bg-gray-50">关于</a>
            </div>
        </div>
    </nav>

    <!-- 主要内容 -->
    <main class="pt-20 pb-12">
        <!-- 头部横幅 -->
        <header class="bg-gradient-to-r from-primary to-primary/80 text-white py-16 md:py-24">
            <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
                <div class="text-center">
                    <h1 class="text-4xl md:text-5xl font-bold mb-4 text-shadow">豆瓣书籍数据可视化</h1>
                    <p class="text-xl md:text-2xl max-w-3xl mx-auto opacity-90">探索2020-2024年间豆瓣最受欢迎的1000本书，通过交互式可视化了解书籍分类和作者分布情况</p>
                </div>
            </div>
        </header>

        <!-- 概览部分 -->
        <section id="overview" class="py-16 bg-white">
            <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
                <div class="text-center mb-12">
                    <h2 class="text-3xl font-bold text-gray-800 mb-4">数据概览</h2>
                    <p class="text-gray-600 max-w-3xl mx-auto">本可视化展示了从2020年到2024年豆瓣最受欢迎的1000本书的数据，帮助您了解这些书籍的分类分布和作者情况。</p>
                </div>

                <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
                    <!-- 统计卡片 1 -->
                    <div class="bg-white rounded-lg shadow-md p-6 card-hover">
                        <div class="flex items-center justify-between mb-4">
                            <h3 class="text-lg font-medium text-gray-700">书籍总数</h3>
                            <i class="fa fa-book text-primary text-2xl"></i>
                        </div>
                        <p class="text-3xl font-bold text-gray-800" id="total-books">1,000</p>
                        <p class="text-sm text-gray-500 mt-2">豆瓣2020-2024年间最受欢迎的书籍</p>
                    </div>

                    <!-- 统计卡片 2 -->
                    <div class="bg-white rounded-lg shadow-md p-6 card-hover">
                        <div class="flex items-center justify-between mb-4">
                            <h3 class="text-lg font-medium text-gray-700">平均评分</h3>
                            <i class="fa fa-star text-secondary text-2xl"></i>
                        </div>
                        <p class="text-3xl font-bold text-gray-800" id="average-rating">8.5</p>
                        <p class="text-sm text-gray-500 mt-2">满分10分</p>
                    </div>

                    <!-- 统计卡片 3 -->
                    <div class="bg-white rounded-lg shadow-md p-6 card-hover">
                        <div class="flex items-center justify-between mb-4">
                            <h3 class="text-lg font-medium text-gray-700">书籍分类</h3>
                            <i class="fa fa-tags text-success text-2xl"></i>
                        </div>
                        <p class="text-3xl font-bold text-gray-800" id="category-count">52</p>
                        <p class="text-sm text-gray-500 mt-2">不同的书籍分类</p>
                    </div>

                    <!-- 统计卡片 4 -->
                    <div class="bg-white rounded-lg shadow-md p-6 card-hover">
                        <div class="flex items-center justify-between mb-4">
                            <h3 class="text-lg font-medium text-gray-700">作者数量</h3>
                            <i class="fa fa-users text-info text-2xl"></i>
                        </div>
                        <p class="text-3xl font-bold text-gray-800" id="author-count">856</p>
                        <p class="text-sm text-gray-500 mt-2">不同的书籍作者</p>
                    </div>
                </div>
            </div>
        </section>

        <!-- 书籍列表部分 -->
        <section id="book-list" class="py-16 bg-gray-50">
            <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
                <div class="text-center mb-12">
                    <h2 class="text-3xl font-bold text-gray-800 mb-4">书籍列表</h2>
                    <p class="text-gray-600 max-w-3xl mx-auto">浏览豆瓣2020-2024年间最受欢迎的1000本书，您可以通过滚动查看所有书籍。</p>
                </div>

                <!-- 搜索和筛选 -->
                <div class="bg-white rounded-lg shadow-md p-6 mb-8">
                    <div class="flex flex-col md:flex-row justify-between items-start md:items-center">
                        <div class="mb-4 md:mb-0 w-full md:w-auto">
                            <h3 class="text-lg font-medium text-gray-800 mb-2">搜索书籍</h3>
                            <div class="relative">
                                <input type="text" id="book-search" placeholder="输入书名、作者或分类..." class="w-full md:w-80 pl-10 pr-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary/50 focus:border-primary">
                                <div class="absolute inset-y-0 left-0 flex items-center pl-3 pointer-events-none">
                                    <i class="fa fa-search text-gray-400"></i>
                                </div>
                            </div>
                        </div>
                        
                        <div class="w-full md:w-auto">
                            <h3 class="text-lg font-medium text-gray-800 mb-2">排序方式</h3>
                            <div class="relative">
                                <select id="book-sort" class="w-full md:w-64 pl-4 pr-10 py-2 border border-gray-300 rounded-md appearance-none focus:outline-none focus:ring-2 focus:ring-primary/50 focus:border-primary">
                                    <option value="rating-desc">评分（高→低）</option>
                                    <option value="rating-asc">评分（低→高）</option>
                                    <option value="title-asc">书名（A→Z）</option>
                                    <option value="title-desc">书名（Z→A）</option>
                                    <option value="author-asc">作者（A→Z）</option>
                                    <option value="author-desc">作者（Z→A）</option>
                                </select>
                                <div class="pointer-events-none absolute inset-y-0 right-0 flex items-center px-2 text-gray-700">
                                    <i class="fa fa-chevron-down text-xs"></i>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>

                <!-- 书籍列表 -->
                <div class="bg-white rounded-lg shadow-md p-6">
                    <div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6" id="books-container">
                        <!-- 书籍卡片将通过JavaScript动态生成 -->
                    </div>
                    
                    <!-- 加载更多按钮 -->
                    <div class="text-center mt-12">
                        <button id="load-more" class="px-6 py-3 bg-primary text-white rounded-md hover:bg-primary/90 transition-colors">
                            加载更多
                        </button>
                    </div>
                </div>
            </div>
        </section>

        <!-- 分类统计部分 -->
        <section id="category-chart" class="py-16 bg-white">
            <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
                <div class="text-center mb-12">
                    <h2 class="text-3xl font-bold text-gray-800 mb-4">书籍分类统计</h2>
                    <p class="text-gray-600 max-w-3xl mx-auto">通过气泡图展示不同书籍分类的数量分布，气泡大小表示该分类的书籍数量。</p>
                </div>

                <!-- 分类图表 -->
                <div class="bg-white rounded-lg shadow-md p-6">
                    <div class="h-[600px] relative">
                        <svg id="category-svg" width="100%" height="100%"></svg>
                        
                        <!-- 工具提示 -->
                        <div id="category-tooltip" class="absolute hidden bg-white p-4 rounded-lg shadow-lg border border-gray-200 max-w-xs">
                            <p id="category-name" class="font-medium text-gray-800"></p>
                            <p id="category-count" class="text-primary font-bold"></p>
                        </div>
                    </div>
                </div>
            </div>
        </section>

        <!-- 作者统计部分 -->
        <section id="author-chart" class="py-16 bg-gray-50">
            <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
                <div class="text-center mb-12">
                    <h2 class="text-3xl font-bold text-gray-800 mb-4">作者统计</h2>
                    <p class="text-gray-600 max-w-3xl mx-auto">通过气泡图展示不同作者的作品数量分布，气泡大小表示该作者的作品数量。</p>
                </div>

                <!-- 作者图表 -->
                <div class="bg-white rounded-lg shadow-md p-6">
                    <div class="h-[600px] relative">
                        <svg id="author-svg" width="100%" height="100%"></svg>
                        
                        <!-- 工具提示 -->
                        <div id="author-tooltip" class="absolute hidden bg-white p-4 rounded-lg shadow-lg border border-gray-200 max-w-xs">
                            <p id="author-name" class="font-medium text-gray-800"></p>
                            <p id="author-count" class="text-primary font-bold"></p>
                        </div>
                    </div>
                </div>
            </div>
        </section>

        <!-- 关于部分 -->
        <section id="about" class="py-16 bg-white">
            <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
                <div class="text-center mb-12">
                    <h2 class="text-3xl font-bold text-gray-800 mb-4">关于本项目</h2>
                    <p class="text-gray-600 max-w-3xl mx-auto">本项目旨在通过数据可视化的方式，帮助人们更好地理解豆瓣平台上的书籍分布情况。</p>
                </div>

                <div class="bg-white rounded-lg shadow-md p-8">
                    <div class="grid grid-cols-1 md:grid-cols-3 gap-8">
                        <div class="text-center">
                            <div class="inline-flex items-center justify-center w-16 h-16 rounded-full bg-primary/10 mb-4">
                                <i class="fa fa-lightbulb-o text-primary text-2xl"></i>
                            </div>
                            <h3 class="text-xl font-medium text-gray-800 mb-2">项目目标</h3>
                            <p class="text-gray-600">通过交互式数据可视化，提供豆瓣平台上书籍的直观展示，帮助用户理解书籍分类和作者分布情况。</p>
                        </div>

                        <div class="text-center">
                            <div class="inline-flex items-center justify-center w-16 h-16 rounded-full bg-primary/10 mb-4">
                                <i class="fa fa-cogs text-primary text-2xl"></i>
                            </div>
                            <h3 class="text-xl font-medium text-gray-800 mb-2">技术实现</h3>
                            <p class="text-gray-600">使用Python爬虫获取豆瓣数据，结合D3.js进行数据可视化，通过Tailwind CSS构建现代化界面。</p>
                        </div>

                        <div class="text-center">
                            <div class="inline-flex items-center justify-center w-16 h-16 rounded-full bg-primary/10 mb-4">
                                <i class="fa fa-github text-primary text-2xl"></i>
                            </div>
                            <h3 class="text-xl font-medium text-gray-800 mb-2">开源项目</h3>
                            <p class="text-gray-600">本项目完全开源，您可以在GitHub上查看源代码并参与贡献，帮助改进数据可视化效果和功能。</p>
                            <div class="mt-4">
                                <a href="#" class="inline-flex items-center justify-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-primary hover:bg-primary/90 transition-colors">
                                    <i class="fa fa-github mr-2"></i> 查看源代码
                                </a>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </section>
    </main>

    <!-- 页脚 -->
    <footer class="bg-gray-800 text-white py-12">
        <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
            <div class="grid grid-cols-1 md:grid-cols-4 gap-8">
                <div>
                    <div class="flex items-center mb-4">
                        <i class="fa fa-book text-primary text-2xl mr-2"></i>
                        <span class="text-xl font-bold">豆瓣书籍可视化</span>
                    </div>
                    <p class="text-gray-400 mb-4">探索豆瓣平台上的书籍世界，发现更多精彩内容。</p>
                    <div class="flex space-x-4">
                        <a href="#" class="text-gray-400 hover:text-white transition-colors">
                            <i class="fa fa-github text-xl"></i>
                        </a>
                        <a href="#" class="text-gray-400 hover:text-white transition-colors">
                            <i class="fa fa-twitter text-xl"></i>
                        </a>
                        <a href="#" class="text-gray-400 hover:text-white transition-colors">
                            <i class="fa fa-linkedin text-xl"></i>
                        </a>
                    </div>
                </div>

                <div>
                    <h3 class="text-lg font-medium mb-4">快速链接</h3>
                    <ul class="space-y-2">
                        <li><a href="#overview" class="text-gray-400 hover:text-white transition-colors">概览</a></li>
                        <li><a href="#book-list" class="text-gray-400 hover:text-white transition-colors">书籍列表</a></li>
                        <li><a href="#category-chart" class="text-gray-400 hover:text-white transition-colors">分类统计</a></li>
                        <li><a href="#author-chart" class="text-gray-400 hover:text-white transition-colors">作者统计</a></li>
                        <li><a href="#about" class="text-gray-400 hover:text-white transition-colors">关于</a></li>
                    </ul>
                </div>

                <div>
                    <h3 class="text-lg font-medium mb-4">资源</h3>
                    <ul class="space-y-2">
                        <li><a href="#" class="text-gray-400 hover:text-white transition-colors">API文档</a></li>
                        <li><a href="#" class="text-gray-400 hover:text-white transition-colors">数据下载</a></li>
                        <li><a href="#" class="text-gray-400 hover:text-white transition-colors">常见问题</a></li>
                        <li><a href="#" class="text-gray-400 hover:text-white transition-colors">技术支持</a></li>
                    </ul>
                </div>

                <div>
                    <h3 class="text-lg font-medium mb-4">订阅更新</h3>
                    <p class="text-gray-400 mb-4">订阅我们的新闻通讯，获取最新的数据可视化案例和技术更新。</p>
                    <form class="flex">
                        <input type="email" placeholder="您的邮箱地址" class="flex-1 px-4 py-2 rounded-l-md focus:outline-none text-gray-800">
                        <button type="submit" class="bg-primary hover:bg-primary/90 text-white px-4 py-2 rounded-r-md transition-colors">
                            订阅
                        </button>
                    </form>
                </div>
            </div>

            <div class="border-t border-gray-700 mt-12 pt-8 text-center text-gray-400">
                <p>&copy; 2024 豆瓣书籍可视化. 保留所有权利。</p>
            </div>
        </div>
    </footer>

    <!-- 返回顶部按钮 -->
    <button id="back-to-top" class="fixed bottom-6 right-6 bg-primary text-white w-12 h-12 rounded-full shadow-lg flex items-center justify-center opacity-0 invisible transition-all duration-300 hover:bg-primary/90">
        <i class="fa fa-arrow-up"></i>
    </button>

    <!-- JavaScript -->
    <script>
        // 导航栏滚动效果
        const navbar = document.getElementById('navbar');
        window.addEventListener('scroll', () => {
            if (window.scrollY > 100) {
                navbar.classList.add('py-2');
                navbar.classList.add('shadow-lg');
            } else {
                navbar.classList.remove('py-2');
                navbar.classList.remove('shadow-lg');
            }
        });

        // 移动端菜单切换
        const menuToggle = document.getElementById('menu-toggle');
        const mobileMenu = document.getElementById('mobile-menu');
        menuToggle.addEventListener('click', () => {
            mobileMenu.classList.toggle('hidden');
        });

        // 平滑滚动
        document.querySelectorAll('a[href^="#"]').forEach(anchor => {
            anchor.addEventListener('click', function (e) {
                e.preventDefault();
                
                const targetId = this.getAttribute('href');
                if (targetId === '#') return;
                
                const targetElement = document.querySelector(targetId);
                if (targetElement) {
                    window.scrollTo({
                        top: targetElement.offsetTop - 80,
                        behavior: 'smooth'
                    });
                    
                    // 如果是移动端，点击后关闭菜单
                    if (!mobileMenu.classList.contains('hidden')) {
                        mobileMenu.classList.add('hidden');
                    }
                }
            });
        });

        // 返回顶部按钮
        const backToTopButton = document.getElementById('back-to-top');
        window.addEventListener('scroll', () => {
            if (window.scrollY > 500) {
                backToTopButton.classList.remove('opacity-0', 'invisible');
                backToTopButton.classList.add('opacity-100', 'visible');
            } else {
                backToTopButton.classList.add('opacity-0', 'invisible');
                backToTopButton.classList.remove('opacity-100', 'visible');
            }
        });

        backToTopButton.addEventListener('click', () => {
            window.scrollTo({
                top: 0,
                behavior: 'smooth'
            });
        });

        // 数据加载和可视化
        let booksData = [];
        let categoryData = [];
        let authorData = [];
        let displayedBooks = [];
        let booksPerPage = 18;
        let currentPage = 1;

        // 加载数据
        async function loadData() {
            try {
                // 加载书籍数据
                const booksResponse = await fetch('data/douban_books.json');
                booksData = await booksResponse.json();
                
                // 加载分类数据
                const categoryResponse = await fetch('data/category_stats.json');
                categoryData = await categoryResponse.json();
                
                // 加载作者数据
                const authorResponse = await fetch('data/author_stats.json');
                authorData = await authorResponse.json();
                
                // 更新概览统计
                updateOverviewStats();
                
                // 初始化书籍列表
                initBookList();
                
                // 初始化分类图表
                initCategoryChart();
                
                // 初始化作者图表
                initAuthorChart();
                
                // 为书籍卡片添加动画
                addBookCardAnimations();
                
            } catch (error) {
                console.error('加载数据失败:', error);
                // 显示错误消息
                document.getElementById('books-container').innerHTML = `
                    <div class="col-span-full text-center py-12">
                        <i class="fa fa-exclamation-circle text-4xl text-danger mb-4"></i>
                        <h3 class="text-xl font-medium text-gray-800 mb-2">加载数据失败</h3>
                        <p class="text-gray-600">请确保数据文件存在且格式正确。</p>
                    </div>
                `;
            }
        }

        // 更新概览统计
        function updateOverviewStats() {
            document.getElementById('total-books').textContent = booksData.length.toLocaleString();
            
            // 计算平均评分
            const totalRating = booksData.reduce((sum, book) => sum + (book.rating || 0), 0);
            const averageRating = booksData.length > 0 ? (totalRating / booksData.length).toFixed(1) : "0.0";
            document.getElementById('average-rating').textContent = averageRating;
            
            document.getElementById('category-count').textContent = categoryData.length;
            document.getElementById('author-count').textContent = authorData.length;
        }

        // 初始化书籍列表
        function initBookList() {
            // 默认排序：评分从高到低
            displayedBooks = [...booksData].sort((a, b) => b.rating - a.rating);
            
            // 渲染第一页书籍
            renderBooks();
            
            // 添加搜索和筛选功能
            const searchInput = document.getElementById('book-search');
            searchInput.addEventListener('input', handleSearch);
            
            const sortSelect = document.getElementById('book-sort');
            sortSelect.addEventListener('change', handleSort);
            
            // 添加加载更多按钮事件
            const loadMoreButton = document.getElementById('load-more');
            loadMoreButton.addEventListener('click', loadMoreBooks);
        }

        // 渲染书籍列表
        function renderBooks() {
            const booksContainer = document.getElementById('books-container');
            booksContainer.innerHTML = '';
            
            const startIndex = (currentPage - 1) * booksPerPage;
            const endIndex = Math.min(startIndex + booksPerPage, displayedBooks.length);
            
            // 显示加载中状态
            if (displayedBooks.length > 0) {
                for (let i = 0; i < Math.min(booksPerPage, displayedBooks.length - startIndex); i++) {
                    const book = displayedBooks[startIndex + i];
                    const bookCard = document.createElement('div');
                    bookCard.className = 'book-card bg-white rounded-lg shadow-sm overflow-hidden transition-all duration-300';
                    bookCard.innerHTML = `
                        <div class="h-64 overflow-hidden">
                            <img src="${book.img_url || 'https://img9.doubanio.com/view/subject/s/public/s1070959.jpg'}" alt="${book.title || '未知书名'}" class="w-full h-full object-cover transition-transform duration-500 hover:scale-110">
                        </div>
                        <div class="p-4">
                            <h3 class="font-bold text-gray-800 mb-1 line-clamp-1">${book.title || '未知书名'}</h3>
                            <p class="text-gray-600 text-sm mb-2 line-clamp-1">${book.author || '未知作者'}</p>
                            <div class="flex items-center mb-2">
                                <div class="flex text-yellow-400">
                                    ${generateStarRating(book.rating || 0)}
                                </div>
                                <span class="text-gray-600 text-sm ml-2">${book.rating || '暂无评分'}</span>
                            </div>
                            <p class="text-gray-500 text-xs line-clamp-2">${book.quote || book.info || '暂无简介'}</p>
                            <div class="mt-3 flex flex-wrap gap-1">
                                ${Array.isArray(book.categories) && book.categories.length > 0 ? 
                                    book.categories.map(category => `
                                        <span class="px-2 py-0.5 bg-primary/10 text-primary text-xs rounded-full">${category}</span>
                                    `).join('') : 
                                    '<span class="px-2 py-0.5 bg-gray-100 text-gray-500 text-xs rounded-full">未分类</span>'
                                }
                            </div>
                        </div>
                    `;
                    booksContainer.appendChild(bookCard);
                }
            } else {
                booksContainer.innerHTML = `
                    <div class="col-span-full text-center py-12">
                        <i class="fa fa-search text-4xl text-gray-400 mb-4"></i>
                        <h3 class="text-xl font-medium text-gray-800 mb-2">没有找到匹配的书籍</h3>
                        <p class="text-gray-600">请尝试使用不同的搜索关键词或筛选条件。</p>
                    </div>
                `;
            }
            
            // 显示或隐藏加载更多按钮
            const loadMoreButton = document.getElementById('load-more');
            if (endIndex >= displayedBooks.length) {
                loadMoreButton.classList.add('hidden');
            } else {
                loadMoreButton.classList.remove('hidden');
            }
            
            // 为新添加的书籍卡片添加动画
            setTimeout(() => {
                document.querySelectorAll('.book-card').forEach((card, index) => {
                    setTimeout(() => {
                        card.classList.add('visible');
                    }, index * 50);
                });
            }, 100);
        }

        // 生成星级评分
        function generateStarRating(rating) {
            const fullStars = Math.floor(rating / 2);
            const hasHalfStar = rating % 2 >= 0.5;
            
            let stars = '';
            for (let i = 0; i < fullStars; i++) {
                stars += '<i class="fa fa-star"></i>';
            }
            
            if (hasHalfStar) {
                stars += '<i class="fa fa-star-half-o"></i>';
            }
            
            const emptyStars = 5 - fullStars - (hasHalfStar ? 1 : 0);
            for (let i = 0; i < emptyStars; i++) {
                stars += '<i class="fa fa-star-o"></i>';
            }
            
            return stars;
        }

        // 处理搜索
        function handleSearch() {
            const searchTerm = document.getElementById('book-search').value.toLowerCase().trim();
            
            if (searchTerm === '') {
                displayedBooks = [...booksData].sort((a, b) => (b.rating || 0) - (a.rating || 0));
            } else {
                displayedBooks = booksData.filter(book => {
                    // 安全地获取书籍属性，避免undefined错误
                    const title = (book.title || '').toLowerCase();
                    const author = (book.author || '').toLowerCase();
                    const categories = Array.isArray(book.categories) ? book.categories.join(' ').toLowerCase() : '';
                    const quote = (book.quote || '').toLowerCase();
                    const info = (book.info || '').toLowerCase();
                    const publisher = (book.publisher || '').toLowerCase();
                    
                    return title.includes(searchTerm) || 
                           author.includes(searchTerm) || 
                           categories.includes(searchTerm) ||
                           quote.includes(searchTerm) ||
                           info.includes(searchTerm) ||
                           publisher.includes(searchTerm);
                });
            }
            
            currentPage = 1;
            renderBooks();
        }

        // 处理排序
        function handleSort() {
            const sortOption = document.getElementById('book-sort').value;
            
            switch (sortOption) {
                case 'rating-desc':
                    displayedBooks.sort((a, b) => b.rating - a.rating);
                    break;
                case 'rating-asc':
                    displayedBooks.sort((a, b) => a.rating - b.rating);
                    break;
                case 'title-asc':
                    displayedBooks.sort((a, b) => a.title.localeCompare(b.title));
                    break;
                case 'title-desc':
                    displayedBooks.sort((a, b) => b.title.localeCompare(a.title));
                    break;
                case 'author-asc':
                    displayedBooks.sort((a, b) => {
                        const authorA = a.author || '';
                        const authorB = b.author || '';
                        return authorA.localeCompare(authorB);
                    });
                    break;
                case 'author-desc':
                    displayedBooks.sort((a, b) => {
                        const authorA = a.author || '';
                        const authorB = b.author || '';
                        return authorB.localeCompare(authorA);
                    });
                    break;
            }
            
            currentPage = 1;
            renderBooks();
        }

        // 加载更多书籍
        function loadMoreBooks() {
            currentPage++;
            renderBooks();
        }

        // 为书籍卡片添加动画
        function addBookCardAnimations() {
            const observer = new IntersectionObserver((entries) => {
                entries.forEach(entry => {
                    if (entry.isIntersecting) {
                        entry.target.classList.add('visible');
                        observer.unobserve(entry.target);
                    }
                });
            }, { threshold: 0.1 });
            
            document.querySelectorAll('.book-card').forEach(card => {
                observer.observe(card);
            });
        }

        // 初始化分类图表
        function initCategoryChart() {
            if (!categoryData || categoryData.length === 0) {
                document.querySelector("#category-chart .h-\\[600px\\]").innerHTML = `
                    <div class="flex flex-col items-center justify-center h-full">
                        <i class="fa fa-exclamation-circle text-4xl text-gray-400 mb-4"></i>
                        <h3 class="text-xl font-medium text-gray-800 mb-2">暂无分类数据</h3>
                        <p class="text-gray-600">未能加载分类统计数据。</p>
                    </div>
                `;
                return;
            }

            const svg = d3.select("#category-svg");
            const width = svg.node().getBoundingClientRect().width;
            const height = svg.node().getBoundingClientRect().height;
            const margin = { top: 50, right: 50, bottom: 50, left: 50 };
            const innerWidth = width - margin.left - margin.right;
            const innerHeight = height - margin.top - margin.bottom;
            
            // 清空SVG
            svg.selectAll("*").remove();
            
            // 创建一个g元素，用于放置图表内容
            const g = svg.append("g")
                .attr("transform", `translate(${margin.left},${margin.top})`);
            
            // 创建标题
            g.append("text")
                .attr("x", innerWidth / 2)
                .attr("y", -20)
                .attr("text-anchor", "middle")
                .attr("font-size", "1.2rem")
                .attr("font-weight", "bold")
                .text("豆瓣书籍分类分布");
            
            // 计算气泡大小范围
            const maxValue = d3.max(categoryData, d => d.value);
            const minValue = d3.min(categoryData, d => d.value);
            
            // 创建气泡大小比例尺
            const radiusScale = d3.scaleSqrt()
                .domain([minValue, maxValue])
                .range([20, 60]);
            
            // 创建颜色比例尺
            const colorScale = d3.scaleOrdinal(d3.schemeCategory10);
            
            // 创建气泡布局
            const simulation = d3.forceSimulation(categoryData)
                .force("x", d3.forceX(innerWidth / 2).strength(0.05))
                .force("y", d3.forceY(innerHeight / 2).strength(0.05))
                .force("collide", d3.forceCollide(d => radiusScale(d.value) + 2).strength(0.7))
                .on("tick", ticked);
            
            // 创建气泡
            const bubbles = g.selectAll(".category-circle")
                .data(categoryData)
                .enter().append("circle")
                .attr("class", "category-circle")
                .attr("r", d => radiusScale(d.value))
                .attr("fill", (d, i) => colorScale(i))
                .attr("opacity", 0.7)
                .on("mouseover", handleMouseOver)
                .on("mousemove", handleMouseMove)
                .on("mouseout", handleMouseOut);
            
            // 添加分类名称
            const labels = g.selectAll(".category-label")
                .data(categoryData)
                .enter().append("text")
                .attr("class", "category-label")
                .attr("text-anchor", "middle")
                .attr("fill", "white")
                .attr("font-size", d => Math.max(8, Math.min(radiusScale(d.value) / 3, 14)))
                .attr("pointer-events", "none")
                .text(d => {
                    // 如果名称太长，截取一部分
                    if (d.name.length > 6) {
                        return d.name.substring(0, 6) + '...';
                    }
                    return d.name;
                });
            
            // 工具提示
            const tooltip = d3.select("#category-tooltip");
            
            function handleMouseOver(event, d) {
                d3.select(this)
                    .transition()
                    .duration(200)
                    .attr("opacity", 1)
                    .attr("stroke", "#000")
                    .attr("stroke-width", 2);
                    
                tooltip.style("display", "block")
                    .style("opacity", 0.9);
                
                tooltip.html(`
                    <p class="font-medium text-gray-800">${d.name}</p>
                    <p class="text-primary font-bold">${d.value} 本书籍</p>
                `);
            }
            
            function handleMouseMove(event) {
                tooltip.style("left", (event.pageX + 15) + "px")
                    .style("top", (event.pageY - 28) + "px");
            }
            
            function handleMouseOut() {
                d3.select(this)
                    .transition()
                    .duration(200)
                    .attr("opacity", 0.7)
                    .attr("stroke", "none");
                    
                tooltip.style("display", "none");
            }
            
            // 气泡位置更新函数
            function ticked() {
                bubbles
                    .attr("cx", d => Math.max(radiusScale(d.value), Math.min(innerWidth - radiusScale(d.value), d.x)))
                    .attr("cy", d => Math.max(radiusScale(d.value), Math.min(innerHeight - radiusScale(d.value), d.y)));
                
                labels
                    .attr("x", d => Math.max(radiusScale(d.value), Math.min(innerWidth - radiusScale(d.value), d.x)))
                    .attr("y", d => Math.max(radiusScale(d.value), Math.min(innerHeight - radiusScale(d.value), d.y)));
            }
        }

        // 初始化作者图表
        function initAuthorChart() {
            if (!authorData || authorData.length === 0) {
                document.querySelector("#author-chart .h-\\[600px\\]").innerHTML = `
                    <div class="flex flex-col items-center justify-center h-full">
                        <i class="fa fa-exclamation-circle text-4xl text-gray-400 mb-4"></i>
                        <h3 class="text-xl font-medium text-gray-800 mb-2">暂无作者数据</h3>
                        <p class="text-gray-600">未能加载作者统计数据。</p>
                    </div>
                `;
                return;
            }

            const svg = d3.select("#author-svg");
            const width = svg.node().getBoundingClientRect().width;
            const height = svg.node().getBoundingClientRect().height;
            const margin = { top: 50, right: 50, bottom: 50, left: 50 };
            const innerWidth = width - margin.left - margin.right;
            const innerHeight = height - margin.top - margin.bottom;
            
            // 清空SVG
            svg.selectAll("*").remove();
            
            // 创建一个g元素，用于放置图表内容
            const g = svg.append("g")
                .attr("transform", `translate(${margin.left},${margin.top})`);
            
            // 创建标题
            g.append("text")
                .attr("x", innerWidth / 2)
                .attr("y", -20)
                .attr("text-anchor", "middle")
                .attr("font-size", "1.2rem")
                .attr("font-weight", "bold")
                .text("豆瓣书籍作者分布");
            
            // 只显示前30位作者，避免图表过于拥挤
            const topAuthors = authorData.slice(0, 30);
            
            // 计算气泡大小范围
            const maxValue = d3.max(topAuthors, d => d.value);
            const minValue = d3.min(topAuthors, d => d.value);
            
            // 创建气泡大小比例尺
            const radiusScale = d3.scaleSqrt()
                .domain([minValue, maxValue])
                .range([15, 40]);
            
            // 创建颜色比例尺
            const colorScale = d3.scaleOrdinal(d3.schemeCategory10);
            
            // 创建气泡布局
            const simulation = d3.forceSimulation(topAuthors)
                .force("x", d3.forceX(innerWidth / 2).strength(0.05))
                .force("y", d3.forceY(innerHeight / 2).strength(0.05))
                .force("collide", d3.forceCollide(d => radiusScale(d.value) + 2).strength(0.7))
                .on("tick", ticked);
            
            // 创建气泡
            const bubbles = g.selectAll(".author-circle")
                .data(topAuthors)
                .enter().append("circle")
                .attr("class", "author-circle")
                .attr("r", d => radiusScale(d.value))
                .attr("fill", (d, i) => colorScale(i % 10))
                .attr("opacity", 0.7)
                .on("mouseover", handleMouseOver)
                .on("mousemove", handleMouseMove)
                .on("mouseout", handleMouseOut);
            
            // 添加作者名称
            const labels = g.selectAll(".author-label")
                .data(topAuthors)
                .enter().append("text")
                .attr("class", "author-label")
                .attr("text-anchor", "middle")
                .attr("fill", "white")
                .attr("font-size", d => Math.max(8, Math.min(radiusScale(d.value) / 3, 12)))
                .attr("pointer-events", "none")
                .text(d => {
                    // 如果名称太长，截取一部分
                    if (d.name.length > 6) {
                        return d.name.substring(0, 6) + '...';
                    }
                    return d.name;
                });
            
            // 工具提示
            const tooltip = d3.select("#author-tooltip");
            
            function handleMouseOver(event, d) {
                d3.select(this)
                    .transition()
                    .duration(200)
                    .attr("opacity", 1)
                    .attr("stroke", "#000")
                    .attr("stroke-width", 2);
                    
                tooltip.style("display", "block")
                    .style("opacity", 0.9);
                
                tooltip.html(`
                    <p class="font-medium text-gray-800">${d.name}</p>
                    <p class="text-primary font-bold">${d.value} 部作品</p>
                `);
            }
            
            function handleMouseMove(event) {
                tooltip.style("left", (event.pageX + 15) + "px")
                    .style("top", (event.pageY - 28) + "px");
            }
            
            function handleMouseOut() {
                d3.select(this)
                    .transition()
                    .duration(200)
                    .attr("opacity", 0.7)
                    .attr("stroke", "none");
                    
                tooltip.style("display", "none");
            }
            
            // 气泡位置更新函数
            function ticked() {
                bubbles
                    .attr("cx", d => Math.max(radiusScale(d.value), Math.min(innerWidth - radiusScale(d.value), d.x)))
                    .attr("cy", d => Math.max(radiusScale(d.value), Math.min(innerHeight - radiusScale(d.value), d.y)));
                
                labels
                    .attr("x", d => Math.max(radiusScale(d.value), Math.min(innerWidth - radiusScale(d.value), d.x)))
                    .attr("y", d => Math.max(radiusScale(d.value), Math.min(innerHeight - radiusScale(d.value), d.y)));
            }
        }

        // 页面加载完成后初始化
        window.addEventListener('DOMContentLoaded', () => {
            loadData();
        });
    </script>
</body>
</html>
    